home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / Libraries / DCLAP 6d / dclap6d / network / ncsasock / sock_tcp.c < prev    next >
Text File  |  1996-07-05  |  22KB  |  972 lines

  1. /*
  2.  * BSD-style socket emulation library for the Mac
  3.  * Original author: Tom Milligan
  4.  * Current author: Charlie Reiman - creiman@ncsa.uiuc.edu
  5.  *
  6.  * This source file is placed in the public domian.
  7.  * Any resemblance to NCSA Telnet, living or dead, is purely coincidental.
  8.  *
  9.  *      National Center for Supercomputing Applications
  10.  *      152 Computing Applications Building
  11.  *      605 E. Springfield Ave.
  12.  *      Champaign, IL  61820
  13.  */
  14.  
  15. #ifdef USEDUMP
  16. # pragma load "Socket.dump"
  17.  
  18. #else
  19. # include <Events.h>
  20. # include <Memory.h>
  21. # include <Types.h>
  22. # include <OSUtils.h> /* for SysBeep */
  23. # include <Events.h> /* for TickCount */
  24. # include <Stdio.h>
  25.  
  26.  
  27. # include <s_types.h>
  28. # include <neti_in.h>
  29. #ifdef COMP_CODEWAR
  30. #define NOWAY
  31. #undef EDOM
  32. #undef ERANGE
  33. #endif
  34. # include <neterrno.h>
  35. # include <s_socket.h>
  36. # include <s_time.h>
  37. # include <s_uio.h>
  38.  
  39. # include "sock_str.h"
  40. # include "sock_int.h" 
  41. # include <unixlib.h>
  42.  
  43. #endif
  44.  
  45. #ifndef THINK_C
  46. # include "sock_int.h"
  47. #endif
  48.  
  49. extern SocketPtr sockets;
  50. extern SpinFn spinroutine;    /* The spin routine. */ 
  51.  
  52. /*
  53.  * sock_tcp_new_stream
  54.  *
  55.  * Create a new tcp stream.
  56.  */
  57. int sock_tcp_new_stream(
  58.     SocketPtr sp)
  59. {
  60.     OSErr        io;
  61.     TCPiopb        pb;
  62.     StreamHashEntPtr shep;
  63.  
  64. #if SOCK_TCP_DEBUG >= 2
  65.     sock_print("sock_tcp_new_stream", sp);
  66. #endif
  67.     
  68.     io = xTCPCreate(STREAM_BUFFER_SIZE, sock_tcp_notify, &pb);
  69.     switch(io)
  70.     {
  71.         case noErr:                 break;
  72.         case streamAlreadyOpen:     return(sock_err(io));
  73.         case invalidLength:         return(sock_err(ENOBUFS));
  74.         case invalidBufPtr:         return(sock_err(ENOBUFS));
  75.         case insufficientResources: return(sock_err(EMFILE));
  76.         default: /* error from PBOpen */ return(sock_err(ENETDOWN));
  77.     }
  78.     sp->sstate = SOCK_STATE_UNCONNECTED;
  79.     sp->peer.sin_family = AF_INET;
  80.     sp->peer.sin_addr.s_addr = 0;
  81.     sp->peer.sin_port = 0;
  82.     bzero(&sp->peer.sin_zero[0], 8);
  83.     sp->dataavail = 0;
  84.     sp->asyncerr = 0;
  85.     sp->stream = pb.tcpStream;
  86.         
  87.     if ((shep = sock_new_shep(sp->stream))!=NULL)
  88.         {
  89.         shep -> stream = sp->stream;
  90.         shep -> socket = sp;
  91.         return(0);
  92.         }
  93.     else
  94.         return -1;
  95. }
  96.  
  97.  
  98. /*
  99.  *    sock_tcp_connect - initiate a connection on a TCP socket
  100.  *
  101.  */
  102. static void sock_tcp_connect_done(TCPiopb *pb);
  103.  
  104. int sock_tcp_connect(
  105.     SocketPtr sp,
  106.     struct sockaddr_in *addr)
  107. {
  108.     OSErr io;
  109.     TCPiopb    *pb;
  110.     
  111. #if SOCK_TCP_DEBUG >= 2
  112.     sock_print("sock_tcp_connect",sp);
  113. #endif
  114.     
  115.     /* Make sure this socket can connect. */
  116.     if (sp->sstate == SOCK_STATE_CONNECTING)
  117.         return(sock_err(EALREADY));
  118.     if (sp->sstate != SOCK_STATE_UNCONNECTED)
  119.         return(sock_err(EISCONN));
  120.         
  121.     sp->sstate = SOCK_STATE_CONNECTING;
  122.     
  123.     if (!(pb= (TCPiopb *)sock_fetch_pb(sp)))
  124.         return sock_err(ENOMEM);
  125.     
  126.     io = xTCPActiveOpen(pb, sp->sa.sin_port,addr->sin_addr.s_addr, addr->sin_port, 
  127.             (TCPIOCompletionProc) sock_tcp_connect_done);
  128.             
  129.     if (io != noErr)
  130.     {
  131.         sp->sstate = SOCK_STATE_UNCONNECTED;
  132.         return(sock_err(io));
  133.     }
  134.     
  135.     if (sp->nonblocking)        
  136.         return(sock_err(EINPROGRESS));
  137.     
  138.     /* sync connect - spin till TCPActiveOpen completes */
  139.  
  140. #if SOCK_TCP_DEBUG >= 5
  141.     dprintf("spinning in connect\n");
  142. #endif
  143.  
  144.     SPIN (pb->ioResult==inProgress,SP_MISC,0L)
  145.  
  146. #if SOCK_TCP_DEBUG >= 5
  147.     dprintf("done spinning\n");
  148. #endif
  149.     
  150.     if ( pb->ioResult != noErr )
  151.         return sock_err(pb->ioResult);
  152.     else
  153.         return 0;
  154. }
  155.  
  156. static void sock_tcp_listen_done(TCPiopb *pb)
  157. {    
  158.     SocketPtr sp;
  159.  
  160.     sp = sock_find_shep(pb->tcpStream)->socket;
  161.     
  162.     switch(pb->ioResult)
  163.     {
  164.         case noErr:
  165.             sp->peer.sin_addr.s_addr = pb->csParam.open.remoteHost;
  166.             sp->peer.sin_port = pb->csParam.open.remotePort;
  167.             sp->sstate = SOCK_STATE_LIS_CON;
  168.             sp->asyncerr = 0;
  169.             break;
  170.             
  171.         case openFailed:
  172.         case invalidStreamPtr:
  173.         case connectionExists:
  174.         case duplicateSocket:
  175.         case commandTimeout:
  176.         default:                
  177.             sp->sstate = SOCK_STATE_UNCONNECTED;
  178.             sp->asyncerr = pb->ioResult;
  179.             break;
  180.     }
  181. }
  182.  
  183. /*
  184.  * sock_tcp_listen() - put s into the listen state.
  185.  */
  186. int sock_tcp_listen(
  187.     SocketPtr sp)
  188. {
  189.     OSErr        io;
  190.     TCPiopb        *pb;
  191.  
  192. #if SOCK_TCP_DEBUG >= 2
  193.     sock_print("sock_tcp_listen",sp);
  194. #endif
  195.  
  196.     if (!(pb = (TCPiopb *)sock_fetch_pb(sp)))
  197.         return (sock_err(ENOMEM));
  198.     
  199.     if (sp->sstate != SOCK_STATE_UNCONNECTED)
  200.         return(sock_err(EISCONN));
  201.     
  202.     sp->sstate = SOCK_STATE_LISTENING;
  203.     
  204.     io = xTCPPassiveOpen(pb, sp->sa.sin_port, 
  205.                 (TCPIOCompletionProc) sock_tcp_listen_done);
  206.     if (io != noErr) 
  207.     {
  208.         sp->sstate = SOCK_STATE_UNCONNECTED;
  209.         return(sock_err(io));
  210.     }
  211. #if SOCK_TCP_DEBUG >= 5
  212.     dprintf("sock_tcp_listen: about to spin for port number - ticks %d\n",
  213.             TickCount());
  214. #endif
  215.     while (pb->csParam.open.localPort == 0)
  216.     {
  217. #if SOCK_TCP_DEBUG >= 5
  218.         tcpCheckNotify();
  219. #endif
  220.         SPIN(false,SP_MISC,0L)
  221.     }
  222. #if SOCK_TCP_DEBUG >= 5
  223.     dprintf("sock_tcp_listen: port number is %d ticks %d\n",
  224.     pb->csParam.open.localPort,TickCount());
  225. #endif
  226.     sp->sa.sin_addr.s_addr = pb->csParam.open.localHost;
  227.     sp->sa.sin_port = pb->csParam.open.localPort;
  228.     return(0);
  229. }
  230.  
  231. /*
  232.  *    sock_tcp_accept()
  233.  */
  234. int sock_tcp_accept(
  235.     SocketPtr sp,
  236.     struct sockaddr_in *from,
  237.     Int4 *fromlen)
  238. {
  239.     int         s1;
  240.     TCPiopb        *pb;
  241.     StreamHashEntPtr shep;
  242.  
  243. #if SOCK_TCP_DEBUG >= 2
  244.     sock_print("sock_tcp_accept",sp);
  245. #endif
  246.  
  247.     if (sp->sstate == SOCK_STATE_UNCONNECTED)
  248.     {
  249.         if (sp->asyncerr != 0)
  250.         {
  251.             (void) sock_err(sp->asyncerr);
  252.             sp->asyncerr = 0;
  253.             return(-1);
  254.         }
  255.         else
  256.             return(sock_err(ENOTCONN));
  257.     }
  258.     if (sp->sstate != SOCK_STATE_LISTENING && sp->sstate != SOCK_STATE_LIS_CON)
  259.         return(sock_err(ENOTCONN));
  260.  
  261.     if (sp->sstate == SOCK_STATE_LISTENING) 
  262.     {    
  263.         if (sp->nonblocking) 
  264.             return(sock_err(EWOULDBLOCK));
  265.  
  266.         /*    Spin till sock_tcp_listen_done runs. */
  267. #if SOCK_TCP_DEBUG >= 5
  268.         dprintf("--- blocking...\n");
  269. #endif
  270.         
  271.         SPIN(sp->sstate == SOCK_STATE_LISTENING,SP_MISC,0L);
  272.  
  273. #if SOCK_TCP_DEBUG >= 5
  274.         dprintf("--- done blocking...\n");
  275. #endif
  276.         
  277.         /* got notification - was it success? */
  278.         if (sp->sstate != SOCK_STATE_LIS_CON) 
  279.         {
  280. #if    SOCK_TCP_DEBUG >=3
  281.             dprintf("--- failed state %04x code %d\n",sp->sstate,sp->asyncerr);
  282. #endif
  283.             (void) sock_err(sp->asyncerr);
  284.             sp->asyncerr = 0;
  285.             return(-1);
  286.         }
  287.     }
  288. #if SOCK_TCP_DEBUG >= 3
  289.     dprintf("sock_tcp_accept: Have connection, peer is %08x/%d, duplicating socket.\n",
  290.             sp->peer.sin_addr,sp->peer.sin_port);
  291. #endif
  292.     /*
  293.      * Have connection.  Duplicate this socket.  The client gets the connection
  294.      * on the new socket and I create a new stream on the old socket and put it 
  295.      * in listen state. 
  296.      */
  297.     sp->sstate = SOCK_STATE_CONNECTED;
  298.  
  299.     s1 = sock_free_fd(0);
  300.     if (s1 < 0) 
  301.     {
  302.         /*    No descriptors left.  Abort the incoming connection. */
  303. #if SOCK_TCP_DEBUG >= 2
  304.         dprintf("sock_tcp_accept: No descriptors left.\n");
  305. #endif
  306.         if (!(pb = (TCPiopb *)sock_fetch_pb (sp)))
  307.             return sock_err(ENOMEM);
  308.             
  309.         (void) xTCPAbort(pb);
  310.         sp->sstate = SOCK_STATE_UNCONNECTED;
  311.  
  312.         /* try and put the socket back in listen mode */
  313.         if (sock_tcp_listen(sp) < 0) 
  314.         {
  315. #if SOCK_TCP_DEBUG >= 1
  316.             dprintf("sock_tcp_accept: sock_tcp_listen fails\n");
  317. #endif
  318.             sp->sstate = SOCK_STATE_UNCONNECTED;
  319.             return(-1);        /* errno already set */
  320.         }
  321.         return(sock_err(EMFILE));
  322.     }
  323.     
  324.     /* copy the incoming connection to the new socket */
  325.     sock_dup_fd(sp->fd,s1);
  326. #if SOCK_TCP_DEBUG >= 3
  327.     dprintf("sock_tcp_accept: new socket is %d\n",s1);
  328.     sock_dump();
  329. #endif
  330.  
  331.     /* quitely adjust the StreamHash table */
  332.     if ((shep = sock_find_shep(sp->stream))!=NULL)
  333.         {
  334.         shep->socket = sockets+s1;        /* point to new socket */
  335.         }
  336.  
  337.     /* Create a new MacTCP stream on the old socket and put it into */
  338.     /* listen state to accept more connections. */
  339.     if (sock_tcp_new_stream(sp) < 0 || sock_tcp_listen(sp) < 0) 
  340.     {
  341. #if SOCK_TCP_DEBUG >= 2
  342.         dprintf("accept: failed to restart old socket\n");
  343. #endif
  344.         /* nothing to listen on */
  345.         sp->sstate = SOCK_STATE_UNCONNECTED;
  346.         
  347.         /* kill the incoming connection */
  348.         if (!(pb= (TCPiopb *)sock_fetch_pb(sockets+s1)))
  349.             return sock_err(ENOMEM);
  350.             
  351.         xTCPRelease(pb);
  352.         sock_clear_fd(s1);
  353.         
  354.         return(-1); /* errno set */
  355.     }
  356. #if SOCK_TCP_DEBUG >= 3
  357.     dprintf("sock_tcp_accept: got new stream\n");
  358.     sock_dump();
  359. #endif
  360.  
  361.     /* return address of partner */
  362.     sock_copy_addr(&sockets[s1].peer, from, fromlen);
  363.  
  364.     return(s1);
  365. }
  366.  
  367. /*
  368.  *    sock_tcp_accept_once()
  369.  */
  370. int sock_tcp_accept_once(
  371.     SocketPtr sp,
  372.     struct sockaddr_in *from,
  373.     Int4 *fromlen)
  374. {
  375.  
  376. #if SOCK_TCP_DEBUG >= 2
  377.     sock_print("sock_tcp_accept_once",sp);
  378. #endif
  379.  
  380.     if (sp->sstate == SOCK_STATE_UNCONNECTED)
  381.     {
  382.         if (sp->asyncerr != 0)
  383.         {
  384.             (void) sock_err(sp->asyncerr);
  385.             sp->asyncerr = 0;
  386.             return(-1);
  387.         }
  388.         else
  389.             return(sock_err(ENOTCONN));
  390.     }
  391.     if (sp->sstate != SOCK_STATE_LISTENING && sp->sstate != SOCK_STATE_LIS_CON)
  392.         return(sock_err(ENOTCONN));
  393.  
  394.     if (sp->sstate == SOCK_STATE_LISTENING) 
  395.     {    
  396.         if (sp->nonblocking) 
  397.             return(sock_err(EWOULDBLOCK));
  398.  
  399.         /*    Spin till sock_tcp_listen_done runs. */
  400. #if SOCK_TCP_DEBUG >= 5
  401.         dprintf("--- blocking...\n");
  402. #endif
  403.         
  404.         SPIN(sp->sstate == SOCK_STATE_LISTENING,SP_MISC,0L);
  405.         
  406. #if SOCK_TCP_DEBUG >= 5
  407.         dprintf("--- done blocking...\n");
  408. #endif
  409.         
  410.         /* got notification - was it success? */
  411.         if (sp->sstate != SOCK_STATE_LIS_CON) 
  412.         {
  413. #if    SOCK_TCP_DEBUG >=3
  414.             dprintf("--- failed state %04x code %d\n",sp->sstate,sp->asyncerr);
  415. #endif
  416.             (void) sock_err(sp->asyncerr);
  417.             sp->asyncerr = 0;
  418.             return(-1);
  419.         }
  420.     }
  421. #if SOCK_TCP_DEBUG >= 3
  422.     dprintf("sock_tcp_accept_once: Have connection, peer is %08x/%d.\n",
  423.             sp->peer.sin_addr,sp->peer.sin_port);
  424. #endif
  425.     /*
  426.      * Have connection.
  427.      */
  428.     sp->sstate = SOCK_STATE_CONNECTED;
  429.     
  430.     /* return address of partner */
  431.     sock_copy_addr(&(sp->peer), from, fromlen);
  432.  
  433.     return(0);
  434. }
  435.  
  436.  
  437. static void sock_tcp_recv_done( TCPiopb *pb);
  438.  
  439. /*
  440.  * sock_tcp_recv()
  441.  *
  442.  * returns bytes received or -1 and errno
  443.  */
  444. int sock_tcp_recv(
  445.     SocketPtr sp,
  446.     char *buffer,
  447.     int buflen,
  448.     int flags)
  449. {
  450. #pragma unused(flags)
  451.     TCPiopb    *pb;
  452.     int iter; /* iteration */
  453.     
  454. #if SOCK_TCP_DEBUG >= 2
  455.     sock_print("sock_tcp_recv",sp);
  456. #endif
  457.  
  458.     /* socket hasn't finished connecting yet */
  459.     if (sp->sstate == SOCK_STATE_CONNECTING)
  460.     {
  461. #if SOCK_TCP_DEBUG >= 5
  462.         dprintf("sock_tcp_recv: connection still in progress\n");
  463. #endif
  464.         if (sp->nonblocking)
  465.             return(sock_err(EWOULDBLOCK));
  466.             
  467.         /* async connect and sync recv? */
  468. #if SOCK_TCP_DEBUG >= 5
  469.         dprintf("sock_tcp_recv: spinning on connect\n");
  470. #endif
  471.  
  472.         SPIN(sp->sstate == SOCK_STATE_CONNECTING,SP_MISC,0L)
  473.  
  474. #if SOCK_TCP_DEBUG >= 5
  475.         dprintf("sock_tcp_recv: done spinning\n");
  476. #endif
  477.     }
  478.         
  479.     /* socket is not connected */
  480.     if (! (sp->sstate == SOCK_STATE_CONNECTED)) 
  481.     {
  482.         /* see if the connect died (pretty poor test) */
  483.         if (sp->sstate == SOCK_STATE_UNCONNECTED && sp->asyncerr != 0)
  484.         {
  485.             (void) sock_err(sp->asyncerr);
  486.             sp->asyncerr = 0;
  487.             return(-1);
  488.         }
  489.  
  490.         /* I guess he just forgot */
  491.         return(sock_err(ENOTCONN));        
  492.     }
  493.             
  494.     if (sp->dataavail == 0)
  495.         sp->dataavail = xTCPBytesUnread(sp);        /* sync. call */
  496. #if SOCK_TCP_DEBUG >= 3
  497.     dprintf("sock_tcp_recv: %d bytes available\n", sp->dataavail);
  498. #endif
  499.     if (sp->nonblocking && sp->dataavail == 0) 
  500.             return(sock_err(EWOULDBLOCK));
  501.  
  502.     sp->torecv              = 1; /* # of bytes to try to receive */
  503.     sp->recvBuf             = buffer;
  504.     sp->recvd               = 0; /* count of bytes received */    
  505.     pb = (TCPiopb *)sock_fetch_pb(sp);
  506.     
  507.     /* make 2 iterations; on 1st try, read 1 byte; on 2nd, read
  508.        all outstanding available bytes, up to buflen ... this mechanism seems to
  509.        be necessary for decent performance, because TCPRcv only completes when one
  510.        of the following takes place:
  511.        * enough data has arrived to fill the receive buffer
  512.        * pushed data arrives
  513.        * urgent data is outstanding
  514.        * some reasonable period passes after the arrival of nonpushed, nonurgent data
  515.        * the amount of data received is greater than or equal to 25 percent of the total
  516.          receive buffering for this stream
  517.        * the command time-out expires
  518.        
  519.        In the case when a caller has requested N bytes, and the data is "normal" TCP
  520.        data, the "reasonable" period must expire before this function will return. This
  521.        "reasonable" period appears to be about one second (MacTCP version 1.1), and is
  522.        not configurable. The hope in the algorithm implemented here is that a reasonable
  523.        amount of data will arrive along with the first byte, and that the caller is
  524.        capable of issuing another read() to obtain more data. The one-second "reasonable"
  525.        delay is thus eliminated.
  526.        
  527.        J. Epstein, NCBI, 06/24/92
  528.     */
  529.     
  530.     for (iter = 0; iter < 2; iter++) {
  531.  
  532.         sp->asyncerr            = inProgress;
  533.     
  534.         xTCPRcv(pb, sp->recvBuf, min (sp->torecv,TCP_MAX_MSG),0, 
  535.                 (TCPIOCompletionProc) sock_tcp_recv_done);
  536.     
  537.         SPIN(sp->torecv&&(pb->ioResult==noErr||pb->ioResult==inProgress),
  538.             SP_TCP_READ,sp->torecv)
  539.         
  540.         if ( pb->ioResult == commandTimeout )
  541.             pb->ioResult = noErr;
  542.  
  543.         switch(pb->ioResult)
  544.         {
  545.             case noErr:
  546. #if SOCK_TCP_DEBUG >= 3
  547.                 dprintf("sock_tcp_recv: got %d bytes\n", sp->recvd);
  548. #endif
  549.                 sp->dataavail = xTCPBytesUnread(sp);
  550.                 sp->asyncerr = noErr;
  551.                 if (sp->dataavail <= 0 || buflen <= 1 || iter == 1)
  552.                     return(sp->recvd);
  553.                 /* loop back and obtain the remaining outstanding data */
  554.                 sp->torecv = min(sp->dataavail, buflen - 1);
  555.                 sp->recvBuf = &buffer[1];
  556.                 break;
  557.                 
  558.             case connectionClosing:
  559. #if SOCK_TCP_DEBUG >= 2
  560.                 dprintf("sock_tcp_recv: connection closed\n");
  561. #endif
  562.                 return (sp->recvd);
  563.                 break;
  564.  
  565.  
  566.             case connectionTerminated:
  567.                 /* The connection is aborted. */
  568.                 sp->sstate = SOCK_STATE_UNCONNECTED;
  569. #if SOCK_TCP_DEBUG >= 1
  570.                 dprintf("sock_tcp_recv: connection gone!\n");
  571. #endif
  572.                 return(sock_err(ENOTCONN));
  573.  
  574.             case commandTimeout: /* this one should be caught by sock_tcp_recv_done */
  575.             case connectionDoesntExist:
  576.             case invalidStreamPtr:
  577.             case invalidLength:
  578.             case invalidBufPtr:
  579.             default:
  580.                 return(sock_err(pb->ioResult));
  581.         }
  582.     }
  583.     return(sock_err(ENOTCONN));
  584. }
  585.  
  586. /*
  587.  *    sock_tcp_can_read() - returns non-zero if data or a connection is available
  588.  *  must also return one if the connection is down to force an exit from the
  589.  *  select routine (select is the only thing that uses this).
  590.  */
  591. int sock_tcp_can_read(SocketPtr sp)
  592.     {
  593.     TCPiopb        *pb;
  594.     
  595.     if (sp->sstate == SOCK_STATE_LIS_CON) 
  596.         return(1);
  597.  
  598.     else if (sp->sstate == SOCK_STATE_CONNECTED) 
  599.         {
  600.         if (!(pb= (TCPiopb *)sock_fetch_pb(sp)))
  601.             return sock_err (ENOMEM);
  602.         
  603.         sp->dataavail = xTCPBytesUnread(sp);
  604.         if (sp->dataavail > 0) 
  605.             return(1);
  606.         }
  607.     else if ( ( sp->sstate == SOCK_STATE_UNCONNECTED ) || 
  608.             ( sp->sstate == SOCK_STATE_CLOSING ) )
  609.         return 1;
  610.  
  611.     return(0);
  612.     }
  613.  
  614. /* avoid using standard library here because size of an int may vary externally */
  615. void *sock_memset(void *s, int c, size_t n)
  616. {
  617.     char *t = (char*)s;
  618.     
  619.     for (; n > 0; n--, t++)
  620.         *t = c;
  621.     return s;
  622. }
  623.     
  624. static void sock_tcp_send_done( TCPiopb *pb);
  625.  
  626. /*
  627.  *    sock_tcp_send() - send data
  628.  *
  629.  *    returns bytes sent or -1 and errno
  630.  */
  631. int sock_tcp_send(
  632.     SocketPtr sp,
  633.     char *buffer,
  634.     int count,
  635.     int flags)
  636. {
  637.     int        bytes,towrite;
  638.     miniwds    *thiswds;
  639.     short    wdsnum;
  640.     TCPiopb    *pb;
  641.     miniwds    wdsarray[TCP_MAX_WDS];
  642.  
  643. #if SOCK_TCP_DEBUG >= 2
  644.     sock_print("sock_tcp_send",sp);
  645. #endif
  646.  
  647.     /* socket hasn't finished connecting yet */
  648.     if (sp->sstate == SOCK_STATE_CONNECTING)
  649.     {
  650. #if SOCK_TCP_DEBUG >= 5
  651.         dprintf("sock_tcp_send: connection still in progress\n");
  652. #endif
  653.         if (sp->nonblocking)
  654.             return(sock_err(EALREADY));
  655.             
  656.         /* async connect and sync send? */
  657. #if SOCK_TCP_DEBUG >= 5
  658.         dprintf("sock_tcp_send: spinning on connect\n");
  659. #endif
  660.         while(sp->sstate == SOCK_STATE_CONNECTING)
  661.         {
  662. #if SOCK_TCP_DEBUG >= 5
  663.             tcpCheckNotify();
  664. #endif
  665.             SPIN(false,SP_MISC,0L)
  666.         }
  667. #if SOCK_TCP_DEBUG >= 5
  668.         dprintf("sock_tcp_send: done spinning\n");
  669. #endif
  670.     }
  671.         
  672.     /* socket is not connected */
  673.     if (! (sp->sstate == SOCK_STATE_CONNECTED)) 
  674.     {
  675.         /* see if a previous operation failed */
  676.         if (sp->sstate == SOCK_STATE_UNCONNECTED && sp->asyncerr != 0)
  677.         {
  678.             (void) sock_err(sp->asyncerr);
  679.             sp->asyncerr = 0;
  680.             return(-1);
  681.         }
  682.  
  683.         /* I guess he just forgot */
  684.         return(sock_err(ENOTCONN));        
  685.     }
  686.     
  687.     if ( (xTCPBytesWriteable(sp) < count) && (sp->nonblocking) )
  688.         return sock_err(EWOULDBLOCK);
  689.         
  690.     bytes=count;    /* save count before we nuke it */
  691.     sock_memset(wdsarray,0,TCP_MAX_WDS*sizeof(miniwds));    /* clear up terminus and mark empty */
  692.     thiswds = wdsarray;
  693.     wdsnum = 0;
  694.     
  695.     while (count > 0)
  696.         {    
  697.         /* make sure the thing that just finished worked ok */
  698.         if (sp->asyncerr != 0)
  699.             {
  700.             (void) sock_err(sp->asyncerr);
  701.             sp->asyncerr = 0;
  702.             return(-1);
  703.             }
  704.  
  705. /*
  706.  * for deBUGging: try replacing TCP_MAX_MSG with a small value (like 7) so
  707.  * you can test that the loop won't choke while waiting for writes to finish
  708.  */
  709.         towrite=min(count,TCP_MAX_MSG);
  710.         
  711.         /* find a clean wds */
  712.         
  713.         while ( thiswds->length != 0 )
  714.             {
  715.             wdsnum = (short)((wdsnum+1)%TCP_MAX_WDS); /* generates compiler warning w/o short - why? */
  716.             if (wdsnum)
  717.                 thiswds++;
  718.             else
  719.                 thiswds = wdsarray;
  720.             SPIN(false,SP_TCP_WRITE,count);    /* spin once */
  721.             }
  722.         
  723.         /* find a clean pb */
  724.         
  725.         if (!(pb= (TCPiopb *)sock_fetch_pb(sp)))
  726.             return sock_err(ENOMEM);
  727.             
  728.         
  729.         thiswds->length = (short)towrite;
  730.         thiswds->ptr=buffer;
  731.                 
  732.         xTCPSend(pb,(wdsEntry *)thiswds,(count <= TCP_MAX_MSG), /* push */
  733.                 flags & MSG_OOB,    /*urgent*/
  734.                  (TCPIOCompletionProc) sock_tcp_send_done);
  735.                 
  736.         SPIN(false,SP_TCP_WRITE,count);
  737.         count -= towrite;
  738.         buffer += towrite;
  739.         }
  740.         
  741.     SPIN(pb->ioResult == inProgress,SP_TCP_WRITE,0);
  742.     
  743.     if ( pb->ioResult == noErr )
  744.         {
  745.         return(bytes);
  746.         }
  747.     else
  748.         return(sock_err(pb->ioResult));
  749. }
  750.  
  751.  
  752. /*
  753.  *    sock_tcp_can_write() - returns non-zero if a write will not block
  754.  * Very lousy check. Need to check (send window)-(unack data).
  755.  */
  756. int sock_tcp_can_write(
  757.     SocketPtr sp)
  758. {
  759.     return (sp->sstate == SOCK_STATE_CONNECTED);
  760. }
  761.  
  762. /*
  763.  *    sock_tcp_close() - close down a socket being careful about i/o in progress
  764.  */
  765. int sock_tcp_close(
  766.     SocketPtr sp)
  767. {
  768.     OSErr io;
  769.     TCPiopb    *pb;
  770.  
  771.  void sock_flush_out(SocketPtr);
  772.  void sock_flush_in(SocketPtr);
  773.     
  774. #if SOCK_TCP_DEBUG >= 2
  775.     sock_print("sock_tcp_close ",sp);
  776. #endif
  777.     
  778.     if (!(pb= (TCPiopb *)sock_fetch_pb(sp)))
  779.         return sock_err(ENOMEM);
  780.         
  781.     sock_flush_out(sp);
  782.     
  783.     /* close the stream */ 
  784.     io = xTCPClose(pb,(TCPIOCompletionProc)(-1));
  785.     
  786. #if SOCK_TCP_DEBUG >= 5
  787.     dprintf("sock_tcp_close: xTCPClose returns %d\n",io);
  788. #endif
  789.  
  790.     switch (io)
  791.     {
  792.         case noErr:
  793.         case connectionClosing:
  794.             break;
  795.         case connectionDoesntExist:
  796.         case connectionTerminated:
  797.             break;
  798.         case invalidStreamPtr:
  799.         default:
  800.             return(sock_err(io));
  801.     }
  802.     
  803.     sock_flush_in(sp);
  804.     
  805.     /* destroy the stream */ 
  806.     if ((io = xTCPRelease(pb)) != noErr)
  807.     {
  808. #if SOCK_TCP_DEBUG >= 5
  809.         dprintf("sock_tcp_close: xTCPRelease error %d\n",io);
  810. #endif
  811.         return(sock_err(io));
  812.     }
  813.     /* check for errors from async writes etc */
  814.     if (( sp->asyncerr != noErr ) && ( sp->asyncerr != connectionTerminated ))
  815.     {
  816. #if SOCK_TCP_DEBUG >= 5
  817.         dprintf("sock_tcp_close: asyncerr %d\n",sp->asyncerr);
  818. #endif
  819.         return(sock_err(sp->asyncerr));
  820.     }
  821.     return(0);
  822. }
  823.  
  824. /* int defaultSpin(spin_msg msg,long param) */
  825.  
  826. static void sock_flush_out(SocketPtr sp) {
  827.     while (xTCPWriteBytesLeft(sp)>0) {
  828.         (*spinroutine)( SP_MISC,0L);
  829.         }
  830.     }
  831.  
  832. static void sock_flush_in(SocketPtr sp) {
  833.     TCPiopb    *pb;
  834.     rdsEntry    rdsarray[TCP_MAX_WDS+1];
  835.     int        passcount;
  836.     const int maxpass =4;
  837.     
  838.     if (!(pb = (TCPiopb *)sock_fetch_pb(sp)))
  839.         return;    
  840.         
  841.     for (passcount=0;passcount<maxpass;passcount++) {
  842.         if (xTCPNoCopyRcv(pb,rdsarray,TCP_MAX_WDS,1,0)==noErr) {
  843.             xTCPBufReturn(pb,rdsarray,0);
  844.             (*spinroutine)( SP_MISC,0L);
  845.             }
  846.         else
  847.             break;
  848.         }
  849.         
  850.     if (passcount == maxpass) {        /* remote side isn't being nice */
  851.         (void)xTCPAbort(pb);        /* then try again */
  852.         
  853.         for (passcount=0;passcount<maxpass;passcount++) {
  854.             if (xTCPNoCopyRcv(pb,rdsarray,TCP_MAX_WDS,1,0)==noErr) {
  855.                 xTCPBufReturn(pb,rdsarray,0);
  856.                 (*spinroutine)( SP_MISC,0L);
  857.                 }
  858.             else
  859.                 break;
  860.             }
  861.         }
  862.     }
  863.     
  864.  
  865. #ifndef COMP_CODEWAR
  866. #pragma segment SOCK_RESIDENT
  867. #endif
  868. /*
  869.  * Interrupt routines - MUST BE IN A RESIDENT SEGMENT! Most important to 
  870.  * MacApp programmers
  871.  */
  872. pascal void sock_tcp_notify(
  873.     StreamPtr tcpStream,
  874.     unsigned short eventCode,
  875.     Ptr userDataPtr,
  876.     unsigned short terminReason,
  877.     struct ICMPReport *icmpMsg)
  878.     {
  879. #pragma unused (userDataPtr,terminReason,icmpMsg)
  880.     register     StreamHashEntPtr    shep;
  881.     
  882.  
  883.     shep = sock_find_shep(tcpStream);
  884.     if ( shep )
  885.         {
  886.         SocketPtr    sp = shep->socket;
  887.         
  888.         if ( eventCode == TCPClosing )
  889.             {
  890.             sp->sstate = SOCK_STATE_CLOSING;
  891.             }
  892.         else if ( eventCode == TCPTerminate )
  893.             {
  894.             sp->sstate = SOCK_STATE_UNCONNECTED;
  895.             }
  896.         }
  897.     }
  898.  
  899. static void sock_tcp_connect_done(TCPiopb *pb)
  900.     {
  901.     SocketPtr sp;
  902.     
  903.     sp = sock_find_shep(pb->tcpStream)->socket;
  904.     
  905.     if (pb->ioResult == noErr )
  906.         {
  907.         sp->sa.sin_addr.s_addr = pb->csParam.open.localHost;
  908.         sp->sa.sin_port = pb->csParam.open.localPort;
  909.         sp->peer.sin_addr.s_addr = pb->csParam.open.remoteHost;
  910.         sp->peer.sin_port = pb->csParam.open.remotePort;
  911.         sp->sstate = SOCK_STATE_CONNECTED;
  912.         sp->asyncerr = noErr;
  913.         }
  914.     }
  915.  
  916.  
  917.  
  918. static void sock_tcp_recv_done( TCPiopb *pb)
  919.     {
  920.     register    readin;
  921.     register    SocketPtr    sp;
  922.  
  923.     sp = sock_find_shep( pb->tcpStream )->socket;;
  924.     
  925.     if (pb->ioResult == noErr)
  926.         {
  927.         readin = pb->csParam.receive.rcvBuffLen;
  928.         sp -> recvBuf += readin;
  929.         sp -> recvd   += readin;
  930.         sp -> torecv  -= readin;
  931.         if ( sp -> torecv )
  932.             {
  933.             xTCPRcv(pb,sp->recvBuf,min(sp -> torecv,TCP_MAX_MSG),1,
  934.                     (TCPIOCompletionProc)sock_tcp_recv_done);
  935.             }
  936.         }
  937.     }
  938.  
  939.  
  940. static void sock_tcp_send_done( TCPiopb *pb)
  941. {    
  942.     SocketPtr sp;
  943.  
  944.     sp = sock_find_shep(pb->tcpStream)->socket;
  945.     
  946.     switch(pb->ioResult)
  947.     {
  948.         case noErr:
  949.             ((wdsEntry *)(pb->csParam.send.wdsPtr))->length = 0;    /* mark it free */
  950.             break;
  951.             
  952.         case ipNoFragMemErr:
  953.         case connectionClosing:
  954.         case connectionTerminated:
  955.         case connectionDoesntExist:
  956.             sp->sstate = SOCK_STATE_UNCONNECTED;
  957.             sp->asyncerr = ENOTCONN;
  958.             break;
  959.             
  960.         case ipDontFragErr:
  961.         case invalidStreamPtr:
  962.         case invalidLength:
  963.         case invalidWDS:
  964.         default:
  965.             sp->sstate = SOCK_STATE_UNCONNECTED;
  966.             sp->asyncerr = pb->ioResult;
  967.             break;
  968.     }
  969.     
  970. }
  971.  
  972.